/* 
 * artist style definition
 *
 * Choi Jonghwan<saxboy@gmail.com>
 * 2006/04/23
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "artist_style.h"
#include "scale.h"

ARTISTSTYLE artiststyle_New(void)
{
	ARTISTSTYLE as=NULL; 
	if ( (as=(ARTISTSTYLE)malloc(sizeof(_ARTISTSTYLE))) !=NULL)
	{
		memset(as, 0, sizeof(_ARTISTSTYLE)); 
	}
	return as; 
}

ARTISTSTYLE artiststyle_NewWithStyle(const char *name)
{
	ARTISTSTYLE as=artiststyle_New(); 
	if (as!=NULL)
	{
		if ( artiststyle_Load(as, name) > 0 )
			return as; 
	}

	artiststyle_Delete(as); 
	return as; 
}

int artiststyle_Delete(ARTISTSTYLE as)
{
	if (as==NULL) return -1; 

	{
		int i=0; 
		for(i=0; i< as->pattern_count; i++)
		{
			pattern_Delete( as->patterns[i]); 
		}
	}

	free(as); 

	return 1; 
}

int artiststyle_AddPattern(ARTISTSTYLE as, PATTERN p)
{
	if (as==NULL) return -1; 
	if (p==NULL) return -1; 

	as->patterns[ as->pattern_count ] = p; 
	as->pattern_count++; 

	return 1; 
}

int artiststyle_Load(ARTISTSTYLE as, const char *name)
{
	if (as==NULL) return -1; 
	if (name==NULL) return -1; 

	/* temporary... */
	if ( strcmp(name, "") == 0 )
	{
		/* dummy... */
	}
	else if ( strcmp(name, "latindrum") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("-latin_drum { d2d2zdzdzdz2d2d2 61 61 61 61 61 61 61 61 61 61 } ")); 
	}
	else if ( strcmp(name, "bebopdrum") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("-bebop_drum{ d4d3dd4d3d 51 51 51 51 51 51 } ")); 
	}
	else if ( strcmp(name, "walkingbass") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass1{ 1Q 5Q 3Q 1Q }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass2{ 5Q 3Q 1Q 1Q }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass3{ 1Q 5Q 5Q 3Q }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass4{ 1Q 1Q 1Q 5Q }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass5{ 5Q 3Q 1Q 5Q }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass6{ 1Q 5Q 1Q 5Q }")); 
	}
	else if ( strcmp(name, "latinbass") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("latin_bass{ 1Q 5Q 1DE 5S 5E 1E }")); 
	}
	else if ( strcmp(name, "rockbass") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("rock_bass{ 1Q 1Q 1Q 1Q }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("rock_bass{ 1E 1E 1E 1E 1E 1E 1E 1E }")); 
	}
	else if ( strcmp(name, "jazzsax") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 1Q 5E 7E 3E 5E 1E 5E}")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 5E 1S 3S 5E 7E 3E 5E 1E 3S 7S }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 1E 3S 7S 1S 3S 7S 5S 7Q 1Q  }")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 2E 6E 2S 5S 6E 2E 3E 5E 2E }")); 

	}
	else if ( strcmp(name, "scaletest") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("scalepat{ 1E 2E 3E 4E 5E 6E 7E 8E }")); 
	}
	else if ( strcmp(name, "melody") == 0 )
	{
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 1E 2E 5E 4E 3H }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 2E 6E 2S 5S 6E 2E 3E 5E 2E }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass4{ 1Q 1Q 1Q 5Q }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 1Q 5E 7E 3E 5E 1E 5E}")); 
		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass3{ 1Q 5Q 5Q 3Q }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("jazz_sax{ 3E 4E 3E 2E 1H }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass5{ 5Q 3Q 1Q 5Q }")); 

		artiststyle_AddPattern( as, 
			pattern_NewWithDefinition("walking_bass5{ 1S 3S 5S 7S 6S 5S 4S 3S 2H }")); 
	}
	else if ( strcmp(name, "jazzpiano") == 0 )
	{
	}
	else
	{
	}

	strcpy(as->name, name); 


	return 1; 
}

/* apply pattern:
 * apply given pattern p to chord c, 
 * and store it to voice v exactly for pattern length
 */
static int _apply_pattern(PATTERN p, CHORD c, VOICE v)
{
	int scale_count=0; 
	SCALE scales[MAX_SCALES_PER_CHORD]= {NULL, }; 
	SCALE selected_scale=NULL; 
	int j=0; 

	if (p==NULL) return -1; 
	if (c==NULL) return -1; 
	if (v==NULL) return -1; 

	//printf("[%s] name=%s\n", __FUNCTION__, p->name); 


	/* get available scales */
	scale_count = chord_GetScaleCount(c); 
	for (j=0; j< scale_count; j++)
	{
		scales[j] = chord_GetScale(c, j); 
	}

	/* FIXME: how can i select scale? */
	selected_scale = scales[0]; 

	{
		int i=0; 

		for (i=0; i< p->elem_count  > 0 ; i++)
		{
			/* get note */
			NOTE n=NULL; 
			if ( selected_scale !=NULL)
			{
				n = note_New(); 
				n->abs_note =scale_GetNote(selected_scale, p->elems[i].pos); 
			}
			else
			{
				/* fall back to chord tones */

				switch ( p->elems[i].pos )
				{
				case 1:
					n = note_Copy( chord_BaseNote(c) ); break;
				case 3:
					n = note_Copy( chord_ThirdNote(c) ); break;
				case 5:
					n = note_Copy( chord_FifthNote(c) ); break;
				case 7:
					n = note_Copy( chord_SeventhNote(c) ); break;
				default:
					/* defaults to root */
					n = note_Copy( chord_BaseNote(c) ); break;
					break;
				}
			}

			note_SetLength( n, p->elems[i].note_length); 
			voice_AddEvent(v, VOICE_EVENT_NOTE, n); 
		}
	}
		

	return 1; 
}

const PATTERN 
artiststyle_SelectPattern(ARTISTSTYLE as, CHORD c, int note_length)
{
	PATTERN p=NULL; 

	if (as==NULL) return NULL; 
	if (c==NULL) return NULL; 


	//printf("[%s] artist style = %s\n", __FUNCTION__, as->name); 

	/* search for phrase patterns exactly for beats */
	{
		int i=0; 
		int candidates[128]={-1,}; 
		int candidates_count=0; 

		for (i=0; i< as->pattern_count; i++)
		{
			PATTERN tp = as->patterns[i]; 

			if ( note_length == tp->pattern_length )
			{
				/* candidate... */
				//p = tp; 

				candidates[ candidates_count++ ] = i; 

				//printf("%% pattern %s matched\n", tp->name); 
			}
		}

		candidates[ candidates_count ] = -1; 

		if ( candidates_count > 0 )
		{
			int r = get_random_number( candidates_count ); 
			p = as->patterns[ candidates[r] ]; 
		}

	}


	/* calc weight for each of the patterns */


	return p; 
}

int artiststyle_ApplyStyleForChord(ARTISTSTYLE as, CHORD c, int note_length, VOICE v)
{
	if (as==NULL) return -1; 
	if (c==NULL) return -1; 
	if (v==NULL) return -1; 

	/* search for available patterns in the style, and apply it */
#if 1
	{
		PATTERN p=NULL; 

		p = artiststyle_SelectPattern(as, c, note_length); 
		if (p==NULL) return -1; 

		pattern_AddCreativity(p); 

		if ( _apply_pattern( p, c, v) < 0 ) return -1; 
	}
#endif

	/* test only... */
#if 0
	{
		PATTERN p=NULL; 
		p = pattern_NewWithDefinition("scalepat{ 1E 2E 3E 4E 5E 6E 7E 8E } "); 
		if (p==NULL) return -1; 

		if ( _apply_pattern( p, c, v) < 0 ) return -1; 

	}
#endif

	return 1; 

}

int artiststyle_ApplyStyleForBar(ARTISTSTYLE as, BAR b, VOICE v)
{
	if (as==NULL) return -1; 
	if (b==NULL) return -1; 
	if (v==NULL) return -1; 

	{
		//int i=0; 

		/* FIXME:... */

		//for (i=0; i< b->measure_count; i++)
		{
			CHORD c = bar_GetChord(b, 0); 

			/* FIXME: calc note length */
			artiststyle_ApplyStyleForChord(as, c, NOTE_WHOLE, v); 
		}
	}

	return 1; 
}

int artiststyle_ApplyStyleForSong(ARTISTSTYLE as, SONG s, VOICE v)
{
	if (as==NULL) return -1; 
	if (s==NULL) return -1; 
	if (v==NULL) return -1; 

	
	{
		int i=0; 

		for (i=0; i< s->bar_count; i++)
		{
			BAR b = s->bars[i]; 

			voice_AddEvent(v, VOICE_EVENT_BAR_START, NULL); 

			artiststyle_ApplyStyleForBar(as, b, v); 
#if 0
			/* FIXME: use bars? */
			{
				CHORD c = bar_GetChord(b, 0); 
				artiststyle_ApplyStyle(style, c, NOTE_WHOLE, v); 
			}
#endif

			voice_AddEvent(v, VOICE_EVENT_BAR_END, NULL); 
		}
	}

	return 1; 
}


int artiststyle_ApplyDrumStyleForSong(ARTISTSTYLE as, SONG s, VOICE v)
{
	if (as==NULL) return -1; 
	if (s==NULL) return -1; 
	if (v==NULL) return -1; 

	/* FIXME: ooopssss */

	if ( v->b_is_drummer )
	{
		PATTERN tp = as->patterns[0]; 
		strncpy( v->drum_pattern, tp->drum_pattern, 128); 
	}

	return 1; 
}

